"""
效果稍差，但是仍然达不到可以使用的地步
"""

import numpy as np
from .binad import BinadHashBuilder
from utils.ellipse import radius_ellipse
import tqdm


class XuHashBuilder(BinadHashBuilder):

    def invariant(self, i, j, C1, C2, N_s=None):
        """
        本函数需要并行化计算
        N_s : 前N_s个最显著的陨石坑
        """
        assert len(C1.shape) == 3 and len(C2.shape) == 3
        N_s = N_s if N_s else np.inf
        # 直接除以行列式是不对滴，参见文献[1] Gros P. , Quan L. .Projective invariants for Vision[R].1992:47
        r = np.array(radius_ellipse(C2)).mean(axis=0)
        ind = np.argsort(r)
        ind = ind[ind < N_s]
        C1 = C1.repeat(len(C2), axis=0)
        det = np.linalg.det(C1) / np.linalg.det(C2)
        I1 = np.trace(C1 @ np.linalg.inv(C2), axis1=1, axis2=2) ** 3 / det
        I2 = np.trace(C2 @ np.linalg.inv(C1), axis1=1, axis2=2) ** 3 * det
        ij = np.array((np.ones_like(j) * i, j))[:, ind]
        I = np.array((I1, I2))[:, ind]
        return ij, I

    def quantize(self, idx, invariants, N_b=4096):
        """设计Hash函数，支持向量化操作
        Args:
            invariants: np.ndarray, shape=(2, N), N为描述子的数量
            N_b: int, 量化的栅格数
        """
        qs = np.log(np.abs(invariants))
        min_qs = qs.min()
        max_qs = qs.max()
        qs = np.int32(np.power((qs - qs.min()) / (qs.max() - qs.min()), 4) * (N_b - 1))
        # 对于相同的值，采用一种简单的方式，全部删除
        # 找出相同的值
        vals, counts = np.unique(qs[0], return_counts=True)
        ind_1 = counts > 1
        ind_2 = np.zeros(qs.shape[1], dtype=bool)
        for val in vals[ind_1]:
            ind = qs[0] == val
            v, c = np.unique(qs[1, qs[0] == val], return_counts=True)
            if not (c > 1).any():
                continue
            ind_2 |= ind & (qs[1] == v[c > 1, None]).any(axis=0)
        print("Totally abundant values: ", ind_2.sum())
        ## 将多个坐标压缩为一个Hash值，检查压缩前后不产生新的冗余值
        hs = qs[0] * N_b + qs[1]
        vals, counts = np.unique(hs, return_counts=True)
        assert counts[counts > 1].sum() == ind_2.sum()
        # 构建哈希表
        ## 由于需要保存全部冗余的陨石坑，因此不能使用线性表，而应当使用类似字典的结构
        ## 这里直接使用dict实现即可，据说pandas有快速字典Series，这里选用pandas的Series
        codebook = {}
        for h in tqdm.tqdm(vals, desc="Preparing Hash Map"):
            # 查找全部可能值
            codebook[h] = idx[:, hs == h].T.tolist()
        return (
            idx,
            codebook,
            {"dist": (min_qs, max_qs), "N_b": N_b},
        )
